package intjavatorpedo.db;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "UserTable", propOrder = {
	"rows",
	"cols",
    "ships",
    "userName",
    "guesses",
    "opponentGuesses"
})
public class UserTable {
	@XmlElement(name = "rows", nillable = false)
	private int rows;
	@XmlElement(name = "cols", nillable = false)
	private int cols;
	@XmlElementWrapper
	@XmlElement(name = "ships", nillable = true)
	private List<Ship> ships;
	@XmlElement(name = "userName", nillable = true)
	private String userName;
	@XmlElementWrapper
	@XmlElement(name = "guesses", nillable = true)
	private List<Coord> guesses;
	@XmlElementWrapper
	@XmlElement(name = "opponentGuesses", nillable = true)
	private List<Coord> opponentGuesses;

	// For serialization
	public UserTable() {
		
	}
	
	public UserTable(String userName) {
		this.userName = userName;
		this.rows = 10;
		this.cols = 10;
		this.ships = new ArrayList<Ship>();
		this.guesses = new ArrayList<Coord>();
		this.opponentGuesses = new ArrayList<Coord>();
	}

	// Returns true if the ship is added (no intersection)
	public boolean addShip(Ship ship) {
		for (Ship s : this.ships) {
			if (s.intersect(ship))
				return false;
		}

		this.ships.add(ship);
		return true;
	}

	// Only add guess if it is a new guess
	public void addGuess(Coord guess) {
		for (Coord c : this.guesses) {
			if (c.equals(guess))
				return;
		}

		this.guesses.add(guess);
	}

	public List<Coord> getGuesses() {
		return guesses;
	}
	
	// Only add guess if it is a new guess
	private void addOpponentGuess(Coord guess) {
		for (Coord c : this.opponentGuesses) {
			if (c.equals(guess))
				return;
		}

		this.opponentGuesses.add(guess);
	}
	
	public List<Coord> getOpponentGuesses() {
		return opponentGuesses;
	}

	public boolean isDead() {
		boolean dead = true;
		for (Ship s : this.ships) {
			if (!s.isDead()) {
				dead = false;
				break;
			}
		}

		return dead;
	}

	public BombResult bomb(Coord bombed) {
		BombResult ret = BombResult.UNSUCCESSFUL;
		for (Ship s : this.ships) {
			if (s.bomb(bombed)) {
				if (s.isDead()) {
					ret = BombResult.SANK;
				} else {
					ret = BombResult.SUCCESSFUL;
				}
				break;
			}
		}
		
		Coord guess = new Coord(bombed.getRow(), bombed.getCol());
		guess.setSuccesfullyBombed(ret != BombResult.UNSUCCESSFUL);
		this.addOpponentGuess(guess);
		
		return ret;
	}
	
	private boolean putOneShip(int row, int col, int length, boolean vertical) {
		int dRow = vertical ? 1 : 0;
		int dCol = vertical ? 0 : 1;
		
		List<Coord> coords = new ArrayList<Coord>();
		for (int i = 0; i < length; i++) {
			Coord c = new Coord(row + i*dRow, col + i*dCol);
			
			if (!c.isInside(this.rows, this.cols))
				return false;
			
			coords.add(c);
		}
		
		return this.addShip(new Ship(coords));
	}
	
	public void putRandomShips() {
		int shipLength[] = new int[]{ 2/*, 2, 3, 4*/ };
		Random r = new Random();
		
		for (int i = 0; i < shipLength.length; i++) {
			while (true) {
				int row = r.nextInt(rows);
				int col = r.nextInt(cols);
				boolean vertical = r.nextBoolean();
				
				// Try to put ship
				if (this.putOneShip(row, col, shipLength[i], vertical))
					break;
			}
		}
	}

	public List<Ship> getShips() {
		return ships;
	}

	public String getUserName() {
		return userName;
	}

	public int getRows() {
		return rows;
	}

	public int getCols() {
		return cols;
	}
}
